import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import classnames from 'classnames';
import WPT from 'wpt';

import { getWebpSrc } from 'modules/image';
import previewImage from 'modules/previewImage';
import { frequency } from 'modules/RX';

import imgManager from './imgManager';

import S from './index.m.less';

import { usePageContext } from '@wpt/showHide/hooks';

/**
 * 检查图片链接是否合法，存在、是字符串类型且不包含`undefined`字符串或路径
 * @param src 图片路径
 * @return {boolean} 是否合法
 */
const validateSrc = (src) => !!src && typeof src === 'string' && src !== 'undefined' && !/\/undefined\//.test(src);

/**
 * 获取paddingTop，让图片按照自己的宽高显示
 * @param src 图片路径
 * @returns {object}
 */
const getImgStyle = (src) => {
    if (!validateSrc(src)) return null;

    const [, w, h] = src.match(/-W(\d+)H(\d+)/) || [];
    let paddingTop = '100%';
    if (w && h) {
        paddingTop = `${(h / w) * 100}%`;
        if (h == 1) {
            paddingTop = '1px';
        }
    }
    return { paddingTop };
};

// 新手拍品 处理图片地址hostname重复的问题
const handleCDNError = (url = '') => url.replace(/^.+(https?:\/\/)/, '$1');

// 获取图片宽高
export const getWH = (src) => {
    const [, w, h] = (validateSrc(src) && src.match(/-W(\d+)H(\d+)/)) || [0, 72, 84];
    return [w, h];
};

/**
 * LazyImage组件附加tips
 * @param {itemTips}
 * @return {*}
 * @constructor
 */
const Tips = ({ itemTips }) => {
    const getTipsStyle = () => {
        const superscript = itemTips.superscript || itemTips.activitySuperscript;
        // 支持superscript?size=2缩放
        const size = Number(WPT.Util.query(superscript).size) || 1;
        const [width, height] = itemTips.activitySuperscript
            ? ['100%', 'auto']
            : getWH(superscript).map((i) => `${i / (100 * size)}rem`);

        return {
            width,
            height,
        };
    };
    const superscript = itemTips.superscript || itemTips.activitySuperscript;
    return superscript ? <img className={S.itemTips} src={superscript} style={getTipsStyle()} /> : null;
};

// 当前视窗高度
const getViewHeight = () => window.innerHeight;

/**
 * 处理src、图片质量、背景显示模式
 * @param src 图片链接
 * @param size 图片质量
 * @param hasBlur 处理图片模糊
 * @return {{showFull: boolean, url: *}}
 */
const resolveSrc = (src, size, hasBlur) => {
    let showFull = false;

    if (hasBlur) {
        // 需要对图片做处理
        const vales = src.match(/-W(\d+)H(\d+)/);
        if (vales) {
            if (vales[2] / vales[1] < 1) {
                src = src.replace(/\/w\/320$/, '/w/640');
                if (size == 320) {
                    size = 640;
                }
            }
            if (vales[2] / vales[1] >= 2.3 || vales[2] / vales[1] <= 0.5) {
                showFull = true;
            }
        }
    }

    // 对于后端已经返回了/w/320，但前端没有指定size的，应当不修改src
    // 在imgSIzeFit对图片的src做判断，看是否需要添加/w/size
    const isDefaultSize = size == 6400;
    if (isDefaultSize) {
        size = 640;
    }

    if (!(/\/w\/\d+$/.test(src) && isDefaultSize)) {
        src = WPT.Util.imgSizeFit(src, size);
    }

    src = getWebpSrc(handleCDNError(src));

    return {
        url: src.replace(/\/w\/0$/, ''),
        showFull,
    };
};

/**
 * 监听全局的 'scroll', 'resize', 'animationend', 'transitionend' 事件以触发LazyImage回调
 */
class EventsHandler {
    _events = {};

    _key = 0;

    constructor() {
        ['scroll', 'resize', 'animationend', 'transitionend'].map((type) => {
            window.addEventListener(type, this.handleEvent, true);
        });
    }

    handleEvent = frequency(() => {
        Object.values(this._events).map((fn) => fn());
    }, 100);

    appendEvent(callback) {
        if (typeof callback !== 'function') return;
        this._key += 1;
        const key = `E-${this._key}`;
        this._events[key] = callback;
        return () => {
            delete this._events[key];
        };
    }
}

const eventHandler = new EventsHandler();

// 获取原始尺寸
const getOriginalUrl = (url = '') => url.replace(/\/w\/\d+$/i, '');

/**
 * 图片懒加载组件
 * @param props
 * @return {*}
 * @constructor
 */
export const LazyImage = (props) => {
    const {
        src, // 图片链接
        className = '',
        style = {},
        hasBlur = true, // 是否处理拍品图片模糊，如果拍品图片是一个长条形状，cdn截得小图会是模糊的
        goodsList = false, // 是不是图墙拍品展示
        autoHeight = false, // 是否让图片高度实际撑开
        maskColor = '#eee', // 设置mask蒙层颜色
        itemTips = {
            superscript: '',
            activitySuperscript: '',
        }, // 设置角标值
        loaded = false, // 图片是否已经被加载
        children = null,
        onStartLoad = null, // 图片即将进入视图开始加载
        onImageShow = null, // 图片进入视窗
        size = 6400,
        preview = false,
        ...other
    } = props;

    const imgRef = useRef(null);
    const [status, setStatus] = useState({
        noAnimation: false,
        loaded,
    });
    // 预览地址
    const [previewUrl, setPreviewUrl] = useState(null);
    // 上下文
    const pageContext = usePageContext();

    const loadImg = useCallback(
        (url, showFull) => {
            imgManager.load(url, ({ fromCache, existUrl }) => {
                setStatus({
                    loaded: true,
                    noAnimation: fromCache || !!status.url,
                    url: existUrl || url,
                    showFull,
                });
            });
        },
        [status]
    );

    useEffect(() => {
        if (process.env.LOCAL && props.size === undefined) {
            console.error('LazyImage应当指定size，size值有0/640/320/240/150/132/96/64/46，其中0表示原图');
        }
    }, []);

    useEffect(() => {
        // src不合法不做处理
        if (!validateSrc(src)) return;

        const { url, showFull } = resolveSrc(src, size, hasBlur);

        !!preview && setPreviewUrl(getOriginalUrl(url));

        // 直接展示背景
        loaded &&
            setStatus({
                ...status,
                url,
                showFull,
            });

        const scrollKey = `LazyImage-${src}-${Math.random().toString(36).substring(2)}`;
        const img = imgRef.current;

        const { $dom, router } = pageContext || {};
        const info = $dom?.info;

        const getRect = () => img.getBoundingClientRect();

        // 开始加载图片【一次性任务】
        let startLoad = (cb) => {
            if (getRect().top < getViewHeight() * 1.5) {
                cb && cb();
                startLoad = null;
                loadImg(url, showFull);
                onStartLoad && onStartLoad();
            }
        };

        // 图片完整进入视图【一次性】
        let imageShow = (cb) => {
            const { top, height } = getRect();
            // 不在页面实例里或者路由匹配时触发onImageShow
            if (top < getViewHeight() - height && router === WPT.curRouter) {
                cb && cb();
                imageShow = null;
                onImageShow && onImageShow(scrollKey, info);
            }
        };

        // 监听事件
        const removeLoadCb = eventHandler.appendEvent(() => {
            startLoad && startLoad(removeLoadCb);
        });
        const removeShowCb = eventHandler.appendEvent(() => {
            imageShow && imageShow(removeShowCb);
        });

        startLoad(removeLoadCb);
        imageShow(removeShowCb);

        return () => {
            removeLoadCb();
            removeShowCb();
        };
    }, [src]);

    // 预览功能逻辑
    useEffect(() => {
        if (!previewUrl || !pageContext?.previewCache) return;

        const { previewCache } = pageContext;
        const img = imgRef.current;

        if (preview === 'page') {
            throw new Error('preview不能使用"page"');
        }

        // 如果传了预览的key，则拼接，否则使用当前路由
        // 当前页面可分块预览
        const previewKey = typeof preview === 'string' ? `pr-${preview}` : `pr-page`;

        if (!previewCache[previewKey]) {
            previewCache[previewKey] = [];
        }

        const previewUrls = previewCache[previewKey];
        previewUrls.push(previewUrl);

        const handlePreview = () => {
            previewImage({
                current: previewUrl,
                urls: previewUrls,
            });
        };
        img?.addEventListener('click', handlePreview);

        return () => {
            const index = previewUrls.findIndex((src) => src === previewUrl);
            index >= 0 && previewUrls.splice(index, 1);
            img?.removeEventListener('click', handlePreview);
        };
    }, [previewUrl]);

    const bgSize = status.showFull && !autoHeight ? { backgroundSize: 'contain' } : {};

    const s = {
        ...style,
        ...(status.loaded && { ...(status.url && { backgroundImage: `url(${status.url})` }), ...bgSize }),
        ...(autoHeight && getImgStyle(status.url || src)),
        ...(maskColor && { color: maskColor }),
    };

    const cn = classnames(
        S.image,
        autoHeight && S.autoHeight,
        status.loaded && [S.loaded, 'imgLoaded'],
        className,
        goodsList && S.goodsList,
        status.noAnimation && S.noAnimation
    );

    return (
        <div ref={imgRef} className={cn} style={s} {...other}>
            <Tips itemTips={itemTips} />
            {children}
        </div>
    );
};

export default function LazyImageWithRep(props) {
    const {
        onImageShow, // 图片出现在视窗内回调
        ...others
    } = props;

    // 上报referrer
    const referrer = useMemo(() => WPT.referrer || document.referrer || '', []);
    // 数据上报逻辑
    const imageShow = (scrollKey, info) => {
        onImageShow && onImageShow(scrollKey, info);
    };

    return <LazyImage {...others} onImageShow={imageShow} />;
}
